import { useCallback, useEffect, useRef, useState } from "react";
import { message } from "antd";
import { InboxOutlined } from "@ant-design/icons";
import "./FileUploader.css";

const MAX_FILE_SIZE = 1024 * 1024 * 1024 * 2;
interface FilePreview {
  url: string;
  type: string;
  name: string;
  size: number;
}

const FileUploader = () => {
  const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
  const [filesPreview, setFilesPreview] = useState<FilePreview[]>([]);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const onDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    e.stopPropagation();
  }, []);
  const onDrop = useCallback(async (e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    e.stopPropagation();
    const items = e.dataTransfer.items;
    const files: File[] = [];
    const traverseEntry = async (entry: FileSystemEntry, files: File[]) => {
      if (entry.isDirectory) {
        const directoryEntry = entry as FileSystemDirectoryEntry;
        const reader = directoryEntry.createReader();
        const entries = await readDirectory(reader);
        for (const subEntry of entries) {
          await traverseEntry(subEntry, files);
        }
      } else {
        const fileEntry = entry as FileSystemFileEntry;
        const file = await new Promise<File>((resolve, reject) =>
          fileEntry.file(resolve, reject)
        );
        files.push(file);
      }
    };
    for (let i = 0; i < items.length; i++) {
      const item = items[i];
      const entry = item.webkitGetAsEntry();
      if (!entry) return;
      await traverseEntry(entry, files);
    }
    checkFiles(files);
  }, []);
  const onClick = useCallback(() => {
    if (!fileInputRef.current) return;
    fileInputRef.current.click();
  }, []);
  const onChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files) return;
    checkFiles(Array.from(files));
    e.target.value = "";
  }, []);
  useEffect(() => {
    return () => {
      filesPreview.forEach((filePreview) => {
        URL.revokeObjectURL(filePreview.url);
      });
    };
  }, [filesPreview]);
  useEffect(() => {
    const filesPreview = selectedFiles.map((file) => {
      const url = URL.createObjectURL(file);
      return {
        url,
        type: file.type,
        name: file.name,
        size: file.size,
      };
    });
    setFilesPreview(filesPreview);
    return () => {
      filesPreview.forEach((filePreview) => {
        URL.revokeObjectURL(filePreview.url);
      });
    };
  }, [selectedFiles]);

  const readDirectory = (
    reader: FileSystemDirectoryReader
  ): Promise<FileSystemEntry[]> => {
    return new Promise((resolve, reject) => {
      const entries: FileSystemEntry[] = [];
      const readEntries = () => {
        reader.readEntries((results) => {
          if (!results.length) {
            resolve(entries);
          } else {
            entries.push(...results);
            readEntries();
          }
        }, reject);
      };
      readEntries();
    });
  };

  const checkFiles = (files: File[]) => {
    const validFiles: File[] = [];
    let hasError = false;
    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      if (file.size > MAX_FILE_SIZE) {
        message.error(`${file.name}文件大小不能超过2G`);
        hasError = true;
        continue;
      }
      if (!(file.type.startsWith("image/") || file.type.startsWith("video/"))) {
        message.error(`${file.name}文件类型必须是图片或者视频`);
        hasError = true;
        continue;
      }
      validFiles.push(file);
    }
    if (!hasError) {
      setSelectedFiles((prevFiles) => [...prevFiles, ...validFiles]);
      message.success(`成功添加${validFiles.length}个文件`);
    }
  };

  const removeFile = (index: number) => {
    setSelectedFiles((prevFiles) => {
      const newFiles = [...prevFiles];
      newFiles.splice(index, 1);
      return newFiles;
    });
  };

  const renderFilesPreview = (filesPreview: FilePreview[]) => {
    if (filesPreview.length > 0) {
      return (
        <div className="preview-grid">
          {filesPreview.map((filePreview, index) => (
            <div key={index} className="preview-item">
              <div className="remove-btn" onClick={() => removeFile(index)}>
                x
              </div>
              {renderFilePreview(filePreview)}
              <div className="file-info">
                {filePreview.name}({formatFileSize(filePreview.size)})
              </div>
            </div>
          ))}
        </div>
      );
    } else {
      return (
        <div className="upload-placeholder" onClick={onClick}>
          <InboxOutlined />
          <p>拖拽文件到此处上传</p>
          <p>支持图片和视频文件,单个文件不超过2GB</p>
        </div>
      );
    }
  };

  const renderFilePreview = (filePreview: FilePreview) => {
    if (filePreview.type.startsWith("video/")) {
      return <video src={filePreview.url} controls />;
    } else if (filePreview.type.startsWith("image/")) {
      return <img src={filePreview.url} alt={filePreview.name} />;
    } else {
      <div className="file-icon">
        <InboxOutlined />
        <div className="file-name">{filePreview.name}</div>
      </div>;
    }
  };

  return (
    <>
      <input
        type="file"
        ref={fileInputRef}
        multiple
        onChange={onChange}
        style={{ display: "none" }}
      />
      <div className="upload-container" onDragOver={onDragOver} onDrop={onDrop}>
        {renderFilesPreview(filesPreview)}
      </div>
    </>
  );
};

const formatFileSize = (bytes: number) => {
  if (bytes === 0) return "0 Bytes";
  const k = 1024;
  const sizes = ["Bytes", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
};

export default FileUploader;
